#pragma once
#include "Buffer.hpp"
#include "Httpresponse.hpp"
#include <map>
#include <string>
#include <functional>
#include "../../../../usr/include/linux/stat.h"
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;

// 当前的解析状态
enum class ProcessState :char
{
    ParseReqLine,
    ParseReqHeaders,
    ParseReqBody,
    ParseReqDone

};
// 定义http请求的结构体
class HttpRequest
{
public:

HttpRequest()
{
    rest();
}

~HttpRequest(){}

// 重置
void rest()
{
    m_curState = ProcessState::ParseReqLine;
    m_method = m_url = m_version = string();
    m_reqHeaders.clear();
}


// 添加请求头
void addHeader( string key, string value)
{
    if (key.empty() || value.empty())
    {
        return;
    }
    m_reqHeaders.insert(make_pair(key, value));
}

// 根据key得到请求头的value
string getHeader(const string key)
{
    auto item = m_reqHeaders.find(key);
    if (item == m_reqHeaders.end())
    {
        return string();
    }

    return item->second;
}

// 解析请求行
bool parseRequestLine(Buffer *readBuf)
{
    // 读出请求行  保存字符串的结束地址
    char *end = readBuf->findCRLE();
    // 保存字符串的起始地址
    char *start = readBuf->data();
    // 请求行总长度
    int linesize = end - start;

    if (linesize > 0)
    {
        auto methodFunc = bind(&HttpRequest::setMethod, this, placeholders::_1);
        start = splitRequestLine(start, end, " ", methodFunc);
        auto urlFunc = bind(&HttpRequest::seturl, this, placeholders::_1);
        start = splitRequestLine(start, end, " ", urlFunc);
        auto versionFunc = bind(&HttpRequest::setVersion, this, placeholders::_1);
        splitRequestLine(start, end, nullptr, versionFunc);
        // 解析请求头做准备
        readBuf->readPosIncrease(linesize + 2);
        // 修改状态
        setState(ProcessState::ParseReqHeaders);
        return true;
    }
    return false;
}

// 解析请求头
bool parseRequestHeader( Buffer *readBuf)
{
    char *end = readBuf->findCRLE();
    if (end != nullptr)
    {
        char *start = readBuf->data();
        int lineSize = end - start;
        // 基于： 搜索字符串
        char *middle = static_cast<char *>(memmem(start, lineSize, ": ", 2));
        if (middle != nullptr)
        {
            int keylen = middle - start;
            int vallen = end - middle - 2;

            if (keylen > 0 && vallen > 0)
            {
                string key(start, keylen);
                string value(middle + 2, vallen);
                addHeader(key, value);
            }
            // 移动读数据的位置
            readBuf->readPosIncrease(lineSize + 2);
        }
        else
        {
            // 请求头已经被解析完成,跳过空行
            readBuf->readPosIncrease(2);

            // 修改解析状态
            //忽略post请求，按照get请求处理
            setState(ProcessState::ParseReqDone);
        }
        return true;
    }
    return false;
}


// 解析http请求协议
bool parseHttpRequest( Buffer *readBuf, HttpResponse *response, Buffer *sendBuf, int socket)
{
    bool flag = true;
    while (m_curState != ProcessState::ParseReqDone)
    {
        switch (m_curState)
        {
        case ProcessState::ParseReqLine:
            flag = parseRequestLine(readBuf);
            break;
        case ProcessState::ParseReqHeaders:
            flag = parseRequestHeader(readBuf);
            break;
        case ProcessState::ParseReqBody:

            break;
        default:
            break;
        }
        if (!flag)
        {
            return flag;
        }
        // 判断是否解析完毕，完毕后准备回复的数据
        if (m_curState == ProcessState::ParseReqDone)
        {
            // 根据解析出的原始数据，对客户端的请求做出处理
            processHttpRequest(response);
            // 组织响应的数据并发送给客户端
            response->prepareMsg(sendBuf, socket);
        }
    }
    m_curState = ProcessState::ParseReqLine; // 状态还原
    return flag;
}

// 处理HTTP请求协议
bool processHttpRequest( HttpResponse *response)
{
    if (strcasecmp(m_method.data(), "get") != 0) // 比较时不区分大小写
    {
        return -1;
    }
    m_url = decodeMsg(m_url);
    // 处理客户端请求的静态资源，要么是一个目录，要么是个文件
    const char *file = nullptr;
    if (strcmp(m_url.data(), "/") == 0)
    {
        file = "./";
    }
    else
    {
        file = m_url.data() + 1;
    }
    // 获取文件属性
    struct stat st;
    int ret = stat(file, &st);
    if (ret == -1)
    {
        // 文件不存在，需要给客户端回复404页面
        // sendHeadMsg(cfd, 404, "Not Found", getFileType(".html"), -1);
        // sendFile("404.html", cfd); // 这个文件必须放在指定目录下才能发送
        response->setFileName("404.html");
        response->setStatusCode(StatusCode::NotFound);
        // 响应头
        response->addHeader("Content-type", getFileType(".html"));
        response->m_sendDataFunc = sendFile;
        return 0;
    }
    response->setFileName(file);
    response->setStatusCode(StatusCode::ok);
    // 存在就要判断文件类型，file是不是目录
    if (S_ISDIR(st.st_mode))
    {
        // 是目录就要将本地的内容发送给客户端
        // sendHeadMsg(cfd, 200, "OK", getFileType(".html"), -1);
        // sendDir(file,cfd);

        // 响应头
        response->addHeader("Content-type", getFileType(".html"));
        response->m_sendDataFunc = sendDir;
    }
    else
    {
        // 把文件的内容发送给客户端
        // sendHeadMsg(cfd, 200, "OK", getFileType(file), st.st_size);
        // sendFile(file, cfd);

        // 响应头
        response->addHeader("Content-type", getFileType(file));
        response->addHeader("Content-length", to_string(st.st_size));
        response->m_sendDataFunc = sendFile;
    }

    return false;
}

// 解码字符串
string decodeMsg(string msg)
{
    string str = string();
    const char *from = msg.data();
    for (; *from != '\0'; ++from)
    {
        if (from[0] == '%' && isxdigit(from[1]) && isxdigit(from[2]))
        {
            str.append(1, hexToDec(from[1] * 16 + hexToDec(from[2])));
            from += 2;
        }
        else
        {
            str.append(1, *from);
        }
    }
    str.append(1, '\0');
}


const string getFileType(const string name)
{
    const char *dot = strrchr(name.data(), '.'); // 该字符串函数是有后向前查找 . 字符，不存在返回NULL
    if (dot == nullptr)
        return "text/plain; cgarset=utf-8";
    if (strcmp(dot, ".html") == 0 || strcmp(dot, "htm") == 0)
        return "text/html; cgarset=utf-8";
    if (strcmp(dot, ".jpg") == 0 || strcmp(dot, "jpeg") == 0)
        return "image/jpeg";
    if (strcmp(dot, "gif") == 0)
        return "image/gif";
    if (strcmp(dot, "png") == 0)
        return "image/png";
    if (strcmp(dot, "css") == 0)
        return "text/css";
    if (strcmp(dot, "au") == 0)
        return "audio/basic";
}
static void sendDir(string dirname, Buffer *sendBuf, int cfd)
{
    // 发送给客户端的html数据的存储位置
    char buf[4096] = {0};
    sprintf(buf, "<html><head><title>%s</title></head><body><table>", dirname.data());
    // 浏览器中显示数据通过html显示，所以通过html标签方式将数据整合在一起发送给客户端，客户端看到的就是一个网页的形式的数据内容
    struct dirent **namelist;
    int num = scandir(dirname.data(), &namelist, NULL, alphasort); // 搜索目录有多少个文件,遍历的文件名都保存到了namelist中
    for (int i = 0; i < num; ++i)
    {
        // 取出文件名，nameliet是一个二级指针，指向的是一个指针数组
        char *name = namelist[i]->d_name;
        struct stat st;
        char subPath[1024] = {0};
        sprintf(subPath, "%s/%s", dirname.data(), name); // 拼接相对路径
        stat(subPath, &st);                              // 第一个参数传相对路径，如果只传name并不是相对路径，name是namelist中的一个子文件
                                                         // 相对路径则需要将namelist和name拼接起来
        // 判断文件属性
        if (S_ISDIR(st.st_mode))
        {
            // 添加a标签使点击目录能跳转到子目录
            sprintf(buf + strlen(buf), "<tr><td><a href=\"%s/\">%s</a></td><td>%ld</td></tr>", name, name, st.st_size);
        }
        else
        {
            sprintf(buf + strlen(buf), "<tr><td><a href=\"%s\">%s</a></td><td>%ld</td></tr>", name, name, st.st_size);
        }
        // send(cfd, buf, strlen(buf), 0);
        sendBuf->appendString(buf);
#ifndef MSG_SEND_AUTO // 如果没有定义这个宏，下面代码有效，否则无效
        sendBuf->sendData(cfd);
#endif
        memset(buf, 0, sizeof(buf));
        // 内存释放，在199行，往第200行传入了199行的二级指针，在调用scandir函数时会有一块可用的内存地址
        // 二级指针中指向的数组元素一个一个的取出后，二级指针指向的那块内存地址就需要被逐个释放，所以这里free写在循环中
        free(namelist[i]);
    }
    // 最后释放这个二级指针的内存地址
    sprintf(buf, "</table></body></html>");
    // send(cfd, buf, strlen(buf), 0);
    sendBuf->appendString(buf);

#ifndef MSG_SEND_AUTO // 如果没有定义这个宏，下面代码有效，否则无效
    sendBuf->sendData(cfd);

#endif
    free(namelist);
}

static void sendFile(string filename, Buffer *sendBuf, int cfd)
{
    int fd = open(filename.data(), O_RDONLY);
    assert(fd > 0);
#if 1
    while (1)
    {
        char buf[1024];
        int len = read(fd, buf, sizeof(buf));
        if (len > 0)
        {
            // send(cfd,buf,len,0);
            sendBuf->appendString(buf, len);
#ifndef MSG_SEND_AUTO // 如果没有定义这个宏，下面代码有效，否则无效
            sendBuf->sendData(cfd);
#endif
            usleep(10); // 控制发送速率，避免大量数据直接到达客户端
        }
        else if (len == 0)
        {
            break;
        }
        else
        {
            close(fd);
            perror("read");
        }
    }
#else
    off_t offset = 0;
    int size = lseek(fd, 0, SEEK_END); // 文件指针移动到尾部，需要再移回头部
    lseek(fd, 0, SEEK_SET);
    while (offset < size)
    {
        int ret = sendfile(cfd, fd, &offset, size - offset);
        printf("ret val:%d", ret);
        if (ret = -1 && errno == EAGAIN)
        {
            printf("没有数据\n");
        }
    }
#endif
}
inline void setMethod(string method)
{
    m_method = method;
}
inline void seturl(string url)
{
    m_url=url;
}
inline void setVersion(string version)
{
    m_version = version;
}
inline void setState(ProcessState state)
{
    m_curState = state;
}
// 获取处理状态
inline ProcessState getState()
{
    return m_curState;
}
private:
char *splitRequestLine(const char *start, const char *end, const char *sub, function<void(string)>callback)
{
    char*space=const_cast<char*>(end);
    if(sub!=nullptr)
    {
        space = static_cast<char*>(memmem(start,end-start,sub,strlen(sub)));
        assert(space !=nullptr);
    }
    int length = space -start;
    callback(string(start,length));
    return space+1;
}
int hexToDec(char c)
{
    if (c >= '0' && c <= '9')
    {
        return c - '0';
    }
    if (c >= 'a' && c <= 'f')
    {
        return c - 'a' + 10;
    }
    if (c >= 'A' && c <= 'F')
    {
        return c - 'A' + 10;
    }
    return 0;
}

private:
    string m_method;
    string m_url;
    string m_version;
    map<string,string>m_reqHeaders; // 结构体数组
    ProcessState m_curState;
};

